/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.channel;

import sun.nio.ch.SelectorProviderImpl;

import java.io.IOException;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayList;
import java.util.List;

import static io.netty.util.internal.ObjectUtil.checkPositive;
import static java.lang.Math.max;
import static java.lang.Math.min;

/**
 * The {@link RecvByteBufAllocator} that automatically increases and
 * decreases the predicted buffer size on feed back.
 * <p>
 * It gradually increases the expected number of readable bytes if the previous
 * read fully filled the allocated buffer.  It gradually decreases the expected
 * number of readable bytes if the read operation was not able to fill a certain
 * amount of the allocated buffer two times consecutively.  Otherwise, it keeps
 * returning the same prediction.
 */
public class AdaptiveRecvByteBufAllocator extends DefaultMaxMessagesRecvByteBufAllocator {

    static final int DEFAULT_MINIMUM = 64;
    // Use an initial value that is bigger than the common MTU of 1500
    static final int DEFAULT_INITIAL = 2048;
    static final int DEFAULT_MAXIMUM = 65536;

    private static final int INDEX_INCREMENT = 4;
    private static final int INDEX_DECREMENT = 1;

    private static final int[] SIZE_TABLE;

    // 下面静态初始化块对SIZE_TABLE进行初始化，
    // 初始化之后为：
    // [16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224,
    // 240, 256, 272, 288, 304, 320, 336, 352, 368, 384, 400, 416,
    // 432, 448, 464, 480, 496, 512, 1024, 2048, 4096, 8192, 16384,
    // 32768, 65536, 131072, 262144, 524288, 1048576, 2097152,
    // 4194304, 8388608, 16777216, 33554432, 67108864, 134217728,
    // 268435456, 536870912, 1073741824]
    // 小于512之前因为缓冲区容量较小，降低步进值，采用每次增加16字节进行分配，
    // 大于512之后则进行加倍分配每次分配缓冲区容量较大，为了减少动态扩张的频率
    // 采用加倍的快速步进分配。
    static {
        List<Integer> sizeTable = new ArrayList<Integer>();
        for (int i = 16; i < 512; i += 16) {
            sizeTable.add(i);
        }

        // Suppress a warning since i becomes negative when an integer overflow happens
        for (int i = 512; i > 0; i <<= 1) { // lgtm[java/constant-comparison]
            sizeTable.add(i);
        }

        SIZE_TABLE = new int[sizeTable.size()];
        for (int i = 0; i < SIZE_TABLE.length; i++) {
            SIZE_TABLE[i] = sizeTable.get(i);
        }
    }

    /**
     * @deprecated There is state for {@link #maxMessagesPerRead()} which is typically based upon channel type.
     */
    @Deprecated
    public static final AdaptiveRecvByteBufAllocator DEFAULT = new AdaptiveRecvByteBufAllocator();

    /**
     * 根据预测的大小，使用二分查找法从SIZE_TABLE中获取预测缓冲区大小对应的最近接的一个值
     * 用于缓冲区的实际分配
     */
    private static int getSizeTableIndex(final int size) {
        for (int low = 0, high = SIZE_TABLE.length - 1; ; ) {
            if (high < low) {
                return low;
            }
            if (high == low) {
                return high;
            }

            int mid = low + high >>> 1;
            int a = SIZE_TABLE[mid];
            int b = SIZE_TABLE[mid + 1];
            if (size > b) {
                low = mid + 1;
            } else if (size < a) {
                high = mid - 1;
            } else if (size == a) {
                return mid;
            } else {
                return mid + 1;
            }
        }
    }

    private final class HandleImpl extends MaxMessageHandle {
        private final int minIndex;
        private final int maxIndex;
        private int index;
        private int nextReceiveBufferSize;
        private boolean decreaseNow;

        /**
         * @param minIndex 64
         * @param maxIndex 65536
         * @param initial 2048
         */
        HandleImpl(int minIndex, int maxIndex, int initial) {
            // 初始值：3
            this.minIndex = minIndex;
            // 初始值：38
            this.maxIndex = maxIndex;

            // 初始值：33
            index = getSizeTableIndex(initial);
            // 初始值：2048
            nextReceiveBufferSize = SIZE_TABLE[index];
        }

        @Override
        public void lastBytesRead(int bytes) {
            // If we read as much as we asked for we should check if we need to ramp up the size of our next guess.
            // This helps adjust more quickly when large amounts of data is pending and can avoid going back to
            // the selector to check for more data. Going back to the selector can add significant latency for large
            // data transfers.
            if (bytes == attemptedBytesRead()) {
                record(bytes);
            }
            super.lastBytesRead(bytes);
        }

        @Override
        public int guess() {
            return nextReceiveBufferSize;
        }


        /**
         * 根据实际读取的字节数，预测下次应该分配的缓冲区大小
         *
         * actualReadBytes是本次实际读取的字节大小，缩小步长INDEX_DECREMENT为1，扩大步长INDEX_INCREMENT为4
         * SIZE_TABLE[max(0, index - INDEX_DECREMENT)]是下一次准备缩小的预测值
         * SIZE_TABLE[min(index + INDEX_INCREMENT, maxIndex)]是下一次准备扩大的预测值
         *
         * 1、缩小场景
         * 初始值index对应的为2048byte，他的下一次准备缩小的预测值为1024byte
         * 假设第一次实际读取actualReadBytes为510byte，小于1024byte，这时候不缩小，但是设置decreaseNow为true
         * 假设第二次实际读取actualReadBytes为460byte，还是小于1024byte
         * 这时候decreaseNow为true，于是设置nextReceiveBufferSize为1024byte【连续两次小于预测值则缩小到预测值】
         *
         * 2、扩大场景
         * 初始值index对应的为2048byte，他的下一次准备扩大的预测值为32768byte
         * 假设第一次实际读取的actualReadBytes为2060byte，则立即设置nextReceiveBufferSize为32768byte
         */
        private void record(int actualReadBytes) {
            // 1、缩小场景
            // SIZE_TABLE[max(0, index - INDEX_DECREMENT)]是尝试缩小的预测值
            // 如果此次实际读取的字节数连续两次小于预测值才调整
            if (actualReadBytes <= SIZE_TABLE[max(0, index - INDEX_DECREMENT)]) {
                // 要求连续两次读取实际字节数恰好等于当前分配
                // 缓冲区剩余可用大小才进行所缩小调整
                if (decreaseNow) {
                    index = max(index - INDEX_DECREMENT, minIndex);
                    nextReceiveBufferSize = SIZE_TABLE[index];
                    decreaseNow = false;
                } else {
                    decreaseNow = true;
                }
            } else if (actualReadBytes >= nextReceiveBufferSize) {
                // 2、扩大场景
                // 实际读取的字节数大于上次预测值，INDEX_INCREMENT=4，步长为4
                // 立即扩大，不需要连续两次大于预测值
                index = min(index + INDEX_INCREMENT, maxIndex);
                nextReceiveBufferSize = SIZE_TABLE[index];
                decreaseNow = false;
            }
        }

        @Override
        public void readComplete() {
            record(totalBytesRead());
        }
    }

    private final int minIndex;
    private final int maxIndex;
    private final int initial;

    /**
     * Creates a new predictor with the default parameters.  With the default
     * parameters, the expected buffer size starts from {@code 1024}, does not
     * go down below {@code 64}, and does not go up above {@code 65536}.
     */
    public AdaptiveRecvByteBufAllocator() {
        this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
    }

    /**
     * Creates a new predictor with the specified parameters.
     *
     * @param minimum the inclusive lower bound of the expected buffer size
     * @param initial the initial buffer size when no feed back was received
     * @param maximum the inclusive upper bound of the expected buffer size
     */
    public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) {
        // 默认minimum（最小缓冲区长度）=64，initial（初始容量）=2048，maximum（最大容量）=65536

        checkPositive(minimum, "minimum");
        if (initial < minimum) {
            throw new IllegalArgumentException("initial: " + initial);
        }
        if (maximum < initial) {
            throw new IllegalArgumentException("maximum: " + maximum);
        }

        // 3
        int minIndex = getSizeTableIndex(minimum);
        if (SIZE_TABLE[minIndex] < minimum) {
            this.minIndex = minIndex + 1;
        } else {
            this.minIndex = minIndex;
        }

        // 38
        int maxIndex = getSizeTableIndex(maximum);
        if (SIZE_TABLE[maxIndex] > maximum) {
            this.maxIndex = maxIndex - 1;
        } else {
            this.maxIndex = maxIndex;
        }

        // 2048
        this.initial = initial;
    }

    @SuppressWarnings("deprecation")
    @Override
    public Handle newHandle() {
        // 3,38,2048
        return new HandleImpl(minIndex, maxIndex, initial);
    }

    @Override
    public AdaptiveRecvByteBufAllocator respectMaybeMoreData(boolean respectMaybeMoreData) {
        super.respectMaybeMoreData(respectMaybeMoreData);
        return this;
    }
}
